import pandas as pd
import numpy as np
import re
import random
from matplotlib import pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import Lasso
from sklearn.ensemble import RandomForestRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.neighbors import KNeighborsRegressor

# 读取CSV文件
df = pd.read_csv(r"C:\Users\Lenovo\Desktop\house.csv", encoding='gbk')

# 删除所有缺失值
d1 = df.dropna().reset_index(drop=True)

# 分解户型
def apart_room(x):
    if pd.isnull(x) or x == '暂无':
        return 0
    return int(x.split('室')[0])


def apart_hall(x):
    # 使用正则表达式查找 '厅' 后面的数字
    match = re.search(r'厅(\d+)', x)
    if match:
        # 如果找到了，返回匹配的数字
        return int(match.group(1))
    else:
        # 如果没有找到，返回 0
        return 0

def apart_wc(x):
    if pd.isnull(x) or '卫' not in x:
        return 0
    try:
        return int(re.search(r'(\d+)卫', x).group(1))
    except AttributeError:
        return 0

# 应用分解
d1['室'] = d1['户型'].map(apart_room)
d1['厅'] = d1['户型'].map(apart_hall)
d1['卫'] = d1['户型'].map(apart_wc)
# 删除楼层、户型、单价
d1.drop(['户型', '楼层', '单价'], axis=1, inplace=True)
# 编码-有序多分类（根据上面可视化的结果，按照对价格的影响程度排序，越大影响越高）
# 无序多分类无法直接引入，必须“哑元”化变量
# 等级变量（有序多分类）可以直接引入模型
map1 = {'南':5, '南北':6, '北':1, '西南':10, '东西':4, '东':2, '东北':8, '东南':9, '西':3, '西北':7}
d1['朝向'] = d1['朝向'].map(map1)
map2 = {'毛坯':1, '简装修':2, '精装修':3, '中装修':4, '豪华装修':5}
d1['装修'] = d1['装修'].map(map2)
map3 = {'有 ':1, '无 ':0}
d1['电梯'] = d1['电梯'].map(map3)
map4 = {' 商品房 ':5, '个人产权':7, '商品房(免税)':7, '使用权':2, '经济适用房':6, ' 成本价房改房 ':3, '限价房':8, '单位产权':7}
d1['产权性质'] = d1['产权性质'].map(map4)
map5 = {'普通住宅':3, '四合院':1, '公寓':2, '商业办公类':4, }
d1['住宅类别'] = d1['住宅类别'].map(map5)
ap5 = {'普通住宅':4, '经济适用房':3, '公寓':1, '商住楼':2, '酒店式公寓':5}
d1['住宅类别'] = d1['住宅类别'].map(map5)
map6 = {'平层':4, '开间':2, '跃层':5, '错层':1, '复式':3}
d1['建筑结构'] = d1['建筑结构'].map(map6)
map7 = {'板楼':4, '钢混':5, '塔板结合':3, '平房':6, '砖混':1, '塔楼':7, '砖楼':2}
d1['建筑类别'] = d1['建筑类别'].map(map7)
map8 = {'北京周边':1,'石景山':2,'怀柔':3,'大兴':4,'丰台':5,'门头沟':6,'海淀':7,'燕郊':8,'通州':9,'房山':10,'密云':11,'昌平':12,'顺义':13,'西城':14,'东城':15,'朝阳':16}
d1['区域'] = d1['区域'].map(map8)
# 删除超过2019年的房子，年代转变为房龄
d1['建筑年代'] = d1['建筑年代'].str.replace('年', '')
# 现在尝试将清理后的列转换为整数类型
d1['建筑年代'] = d1['建筑年代'].astype('int32')
d1.drop(d1[d1['建筑年代'] > 2019].index, inplace=True)
d1['房龄'] = d1['建筑年代'].map(lambda x: 2020-x)
d1.drop(labels=['建筑年代'], axis=1, inplace=True)

X = d1.drop(['总价'], axis=1)
y = d1['总价']

# 确保所有特征都是数值类型，并处理 NaN、无穷大或过大的数值
for col in X.columns:
    # 检查列中是否有 NaN 值
    if X[col].isnull().any():
        # 用列的中位数替换 NaN 值
        median_value = X[col].median()
        X[col] = X[col].fillna(median_value)

    # 检查并替换无穷大值
    X[col] = X[col].replace([np.inf, -np.inf], np.nan)
    X[col] = X[col].fillna(0)  # 用0或其他适当的值替换无穷大值

    # 如果有列包含非数值型数据，需要转换为数值型
    # 这里以 '面积' 为例，假设它包含 '平米' 单位
    if X[col].dtype == object:
        X[col] = X[col].str.replace('平米', '').astype(float)

# 再次检查确保没有 NaN 值
if X.isnull().values.any():
    X = X.dropna(axis='columns')

# 分割数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=33)

# 使用 PolynomialFeatures 之前确保所有特征都是数值类型
X_train_numeric = X_train.select_dtypes(include=[np.number])
X_test_numeric = X_test.select_dtypes(include=[np.number])

# 创建 PolynomialFeatures 实例
poly = PolynomialFeatures(degree=2)

# 应用多项式特征转换
x_train = poly.fit_transform(X_train_numeric)
x_test = poly.transform(X_test_numeric)


# 套索回归

la = Lasso(alpha=0.1,max_iter=100000)
la.fit(x_train,y_train)
print(f'套索回归训练集得分：{round(la.score(x_train,y_train),2)}')
print(f'测试集得分：{round(la.score(x_test,y_test),2)}')

# 随机森林

rf = RandomForestRegressor()
rf.fit(x_train,y_train)
print(f'随机森林训练集得分：{round(rf.score(x_train,y_train),2)}')
print(f'测试集得分：{round(rf.score(x_test,y_test),2)}')

# 决策树

dt = DecisionTreeRegressor(max_depth = 6)
dt.fit(x_train,y_train)
print(f'决策树训练集得分：{round(dt.score(x_train,y_train),2)}')
print(f'测试集得分：{round(dt.score(x_test,y_test),2)}')
# k近邻

kn = KNeighborsRegressor(n_neighbors=20)
kn.fit(x_train,y_train)
print(f'k近邻训练集得分：{round(kn.score(x_train,y_train),2)}')
print(f'测试集得分：{round(kn.score(x_test,y_test),2)}')

apply = np.array([95,10,4,0,5,4,4,5,6,1,3,1,1,10]).reshape(1,-1)
poly_apply = poly.fit_transform(apply)
print('------------总价预测结果-------------')
print(f'线性回归：{round(la.predict(poly_apply)[0],2)}万元')
print(f'随机森林回归：{round(rf.predict(poly_apply)[0],2)}万元')
print(f'决策树回归：{round(dt.predict(poly_apply)[0],2)}万元')
print(f'K近邻回归：{round(kn.predict(poly_apply)[0],2)}万元')
print('------------综合预测结果-------------')
print(round(((la.predict(poly_apply)+rf.predict(poly_apply)+dt.predict(poly_apply)+kn.predict(poly_apply))/4.0)[0],2),'万元')



















